<?php

namespace app\common\validate;

use app\common\service\JsonService;
use think\Validate;

/**
 * 通用的验证规则定义
 */
class BaseValidate extends Validate
{
    protected $method = 'GET';

    /**
     * 设置并校验GET请求方式
     * @return $this
     */
    public function fromGet()
    {
        if (!$this->request->isGet()) {
            JsonService::throw('请求方式有误，请使用GET方式');
        }
        $this->method = 'GET';
        return $this;
    }

    /**
     * 设置并校验POST请求方式
     * @return $this
     */
    public function fromPost()
    {
        if (!$this->request->isPost()) {
            JsonService::throw('请求方式有误，请使用Post方式');
        }
        $this->method = 'Post';
        return $this;
    }

    /**
     * 校验数据
     * @param string|null $scene 验证场景
     * @param bool $batch 是否批量验证（批量验证：一次性验证所有的字段数据，并返回全部的错误）
     * @param array $validateData 额外的验证数据
     * @return array 返回验证成功后的数据
     */
    public function toCheck(string|null $scene = null, bool $batch = false, array $validateData = []): array
    {
        //1.接收参数
        $params = $this->method == 'GET' ? $this->request->get() : $this->request->post();

        //2.合并验证参数
        $params = array_merge($params, $validateData);


        //3.按场景进行验证
        if ($scene) {
            $res = $this->batch($batch)->scene($scene)->check($params);
        } else {
            $res = $this->batch($batch)->check($params);
        }

        //4.验证失败的情况（抛出异常）
        if (!$res) {
            $exception = is_array($this->error) ? implode(';', $this->error) : $this->error;
            JsonService::throw($exception);
        }

        // 5.成功返回数据
        return $params;
    }


    /**
     * 自定义日期检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 用该验证方法可以替代官方的多个日期的验证规则
     */
    public function checkDate(mixed $value, string $rule = ''): bool|string
    {
        $rule = $rule ?: 'Y-m-d';

        //检测是否符合正确的日期要求
        if (true !== $this->is($value, 'date')) {
            return false;
        }

        //检测日期的格式是否正确
        if (true !== $this->dateFormat($value, $rule)) {
            return false;
        }

        //进一步检测日期的格式
        if (date($rule, strtotime($value)) != $value) {
            return false;
        }

        return true;
    }

    /**
     * 自定义时间检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @param array $data 全部数据
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 验证的格式固定是例如：12:05:05
     */
    public function checkTime(mixed $value, string $rule = '', array $data = []): bool|string
    {
        $rule = $rule ?: 'H:i:s';

        //进一步检测时间的格式
        if (date($rule, strtotime($value)) != $value) {
            return false;
        }

        return true;
    }

    /**
     * 自定义日期时间检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @param array $data 全部数据
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note:
     */
    public function checkDateTime(mixed $value, string $rule = '', array $data = []): bool|string
    {
        $rule = $rule ?: 'Y-m-d H:i:s';
        //检测是否符合正确的日期要求
        if (true !== $this->is($value, 'date')) {
            return false;
        }

        //进一步检测日期的格式
        if (date($rule, strtotime($value)) != $value) {
            return false;
        }

        return true;
    }

    /**
     * 自定义多个日期检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @param array $data 全部数据
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 多个日期用英文逗号分割
     */
    public function checkMultiDate(mixed $value, string $rule = '', array $data = []): bool|string
    {
        $rule = $rule ?: 'Y-m-d';
        $dates = explode(',', $value);

        if (empty($dates) || !is_array($dates)) {
            return false;
        }

        //逐一检测单个日期是否正确
        foreach ($dates as $date) {
            if (true !== $this->checkDate($date, $rule)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 自定义日期区间检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @param array $data 全部数据
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 用该验证方法可以替代官方的多个日期的验证规则
     */
    public function checkDateRange(mixed $value, string $rule = '', array $data = []): bool|string
    {
        $rule = $rule ?: 'Y-m-d';

        $delimiter = ' - ';//日期区间的分隔符
        if (!strpos($value, $delimiter)) {
            return false;
        }

        list($before_date, $after_date) = explode($delimiter, $value);

        //检测是否符合正确的日期要求
        if (true !== $this->checkDate($before_date, $rule, $data)) {
            return false;
        }

        if (true !== $this->checkDate($after_date, $rule, $data)) {
            return false;
        }

        //比较日期的大小
        if (strtotime($before_date) > strtotime($after_date)) {
            return false;
        }

        return true;
    }

    /**
     * 自定义日期区间检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @param array $data 全部数据
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 用该验证方法可以替代官方的多个日期的验证规则
     */
    public function checkDateTimeRange(mixed $value, string $rule = '', array $data = []): bool|string
    {
        $rule = $rule ?: 'Y-m-d H:i:s';

        $delimiter = ' - ';//日期区间的分隔符
        if (!strpos($value, $delimiter)) {
            return false;
        }

        list($before_date, $after_date) = explode($delimiter, $value);

        //检测是否符合正确的日期要求
        if (true !== $this->checkDatetime($before_date, $rule, $data)) {
            return false;
        }

        if (true !== $this->checkDatetime($after_date, $rule, $data)) {
            return false;
        }

        //比较日期的大小
        if (strtotime($before_date) > strtotime($after_date)) {
            return false;
        }

        return true;
    }

    /**
     * 自定义月份检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 格式：01或12，都是两个数字
     */
    public function checkMonth(mixed $value, string $rule = ''): bool|string
    {
        if (!preg_match('/^0[1-9]|1[0-2]$/', $value)) {
            return false;
        }

        return true;
    }

    /**
     * 自定义年月份检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 格式：2020-01
     */
    public function checkYearMonth(mixed $value, string $rule = ''): bool|string
    {
        $rule = $rule ?: 'Y-m';

        //检测日期的格式是否正确
        if (true !== $this->dateFormat($value, $rule)) {
            return false;
        }

        //进一步检测日期的格式
        if (date($rule, strtotime($value)) != $value) {
            return false;
        }

        return true;
    }

    /**
     * 自定义颜色检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 格式： 1：#rrggbb  2：rgb(r,g,b)  3：rgba(r,g,b,p)
     */
    public function checkColor(mixed $value, string $rule = ''): bool|string
    {
        $rules = [
            1 => '^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$',
            2 => '^[rR][gG][Bb][Aa]?[\(]((2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?),){2}(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?),?(0\.\d{1,2}|1|0)?[\)]{1}$'
        ];

        foreach ($rules as $rule) {
            if (preg_match('/' . $rule . '/', $value)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 自定义登录用户名检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 格式： 由数字、字母、下划线(一种多多种组成，且只能字母开头)，只验证格式，不验证长度
     */
    public function checkUsername(mixed $value, string $rule = ''): bool|string
    {
        if (!preg_match('/^[A-Za-z][A-Za-z0-9_]+$/', $value)) {
            return false;
        }

        return true;
    }

    /**
     * 自定义登录密码检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 格式： 由数字、字母、下划线、特殊符号!@#$%^&*()_+(一种多多种组成)，只验证格式，不验证长度
     */
    public function checkPassword(mixed $value, string $rule = ''): bool|string
    {
        if (!preg_match('/^[\S]+$/', $value)) {
            return false;
        }

        return true;
    }

    /**
     * 自定义小数检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 可以检测整数和小数，主要是便于验证金额等小数,rule中可以传入小数点前几位和小数点后几位，用英文逗号分隔，不允许小数点结尾的值
     */
    public function checkFloat(mixed $value, string $rule = ''): bool|string
    {
        $prefix_length = 8;//小数点前位数
        $postfix_length = 2;//小数点后位数

        if (!empty($rule) && is_string($rule) && strpos($rule, ',')) {
            list($prefix_length, $postfix_length) = explode(',', $rule);
        }

        //没有小数的情况
        $regx = '/^-?(0|[1-9][0-9]{0,' . ($prefix_length - 1) . '})$/';

        if ($postfix_length >= 1) {
            //有小数的情况
            $regx = '/^-?(0|[1-9][0-9]{0,' . ($prefix_length - 1) . '})(\.[0-9]{1,' . $postfix_length . '})?$/';
        }

        if (!preg_match($regx, $value)) {
            return false;
        }

        return true;
    }

    /**
     * 自定义多个ID检测
     * @param mixed $value 验证的值
     * @param string $rule 验证规则
     * @return bool|string
     * Author: LCY<409028735@qq.com>
     * Time: 2020/11/25 11:33
     * Note: 数组形式 或 多个id用英文逗号分隔，例如：1,2,3，可应用于批量操作的场景
     */
    public function checkIds(mixed $value, string $rule = ''): bool|string
    {
        if (is_string($value)) {
            $value = explode(',', $value);
        }
        foreach ($value as $v) {
            if (!is_numeric($v)) {
                return false;
            }

            $val = 0 + $v;
            if (!is_int($val) || $val < 0) {
                return false;
            }
        }
        return true;
    }
}